using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using TimHeuer.WindowsLiveWriter.S34Writer.S3Model;
using System.IO;
using System.Net;

namespace TimHeuer.WindowsLiveWriter.S34Writer
{
    public partial class S3Browser : Form
    {
        private S3Context _context;
        private S3ListViewItemSorter _listViewSorter = new S3ListViewItemSorter();
        private ThreeSharpWrapper _s3Wrapper = null;
        private S3Bucket _bucket = null;
        private S3Object _object = null;
        private string _htmlFragment = String.Empty;
        private IDictionary<string, IList<S3Object>> _s3Cache = new Dictionary<string, IList<S3Object>>();

        private const string BUCKET_GROUP_NAME = "BucketsGroup";

        public S3Browser()
        {
            InitializeComponent();
        }

        public string HtmlFragment
        {
            get { return _htmlFragment; }
            set { _htmlFragment = value; }
        }

        public void SetContext(S3Context context)
        {
            _context = context;

            _s3Wrapper = new ThreeSharpWrapper(context.AccessKeyId, context.SecretAccessKey);
            _s3Wrapper.IsSecure = false;
            _s3Wrapper.UsePublicUri = true;
        }

        private S3Object CurrentObject
        {
            get { return _object; }
            set
            {
                _object = value;
                UpdateSelections();
            }
        }

        private S3Bucket CurrentBucket
        {
            get { return _bucket; }
            set
            {
                _bucket = value;
                UpdateSelections();
            }
        }

        private void UpdateSelections()
        {
            bool bucketSelected = (_bucket != null);
            bool objectSelected = (_object != null);

            // We can refresh the bucket list if we have a bucket selected
            RefreshButton.Enabled = bucketSelected;
            // We can add if a bucket was selected,
            AddButton.Enabled = bucketSelected;
            // and we can perform our actions if an object is selected.
            InsertButton.Enabled = InsertImageButton.Enabled = bucketSelected && objectSelected;
            // The context menu is enabled if an item is selected
            deleteToolStripMenuItem.Enabled = bucketSelected && objectSelected;
        }

        protected override bool ProcessDialogKey(Keys keyData)
        {
            if (keyData == Keys.Escape)
            {
                // Cancel out of the dialog on the ESC keypress
                DialogResult = DialogResult.Cancel;
            }

            return base.ProcessDialogKey(keyData);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            FileList.ListViewItemSorter = _listViewSorter;
            backgroundWorkerBucketLoader.RunWorkerAsync(_s3Wrapper);
        }

        private void LoadBuckets(IList<S3Bucket> buckets)
        {
            // Clear the list
            BucketListBox.Items.Clear();

            try
            {
                if (buckets != null)
                {
                    BucketListBox.BeginUpdate();
                    foreach (S3Bucket bucket in buckets)
                    {
                        BucketListBoxItem item = new BucketListBoxItem(bucket.Name);
                        item.Tag = bucket;
                        BucketListBox.Items.Add(item);
                    }
                    BucketListBox.EndUpdate();
                }
            }
            catch (S3Exception)
            {
                // TODO: Indicate the error on the UI
            }
        }

        private void BucketListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            BucketListBoxItem item = BucketListBox.SelectedItem as BucketListBoxItem;

            // New selected bucket
            if (item != null)
            {
                CurrentBucket = item.Tag as S3Bucket;
                S3Bucket currentBucket = FileList.Tag as S3Bucket;
                // Ensure that the selected bucket is not the bucket being shown in the FileList.
                // This can happen if the user deselected any items and re-selected the same item,
                // and we want to avoid an unnecessary server query.
                if (CurrentBucket != currentBucket)
                {
                    LoadBucketContents(CurrentBucket);
                }
            }
        }

        private void LoadBucketContents(S3Bucket bucket)
        {
            LoadBucketContents(bucket, false);
        }

        private void LoadBucketContents(S3Bucket bucket, bool force)
        {
            this.Cursor = Cursors.WaitCursor;
            FileList.Items.Clear();
            CurrentObject = null;
            BucketListBox.Enabled = false;
            FileList.Tag = bucket;

            // First clear the cache, if we are forcing it
            if (force)
            {
                if (_s3Cache.ContainsKey(bucket.Name))
                {
                    _s3Cache.Remove(bucket.Name);
                }
            }

            backgroundWorker.RunWorkerAsync(bucket);
        }

        private void PopulateObjectListView(IList<S3Object> objects)
        {
            FileList.Items.Clear();
            CurrentObject = null;

            if (objects != null)
            {
                ListViewItem item;
                ListViewItem.ListViewSubItem[] subItems;

                FileList.BeginUpdate();

                foreach (S3Object obj in objects)
                {
                    item = new ListViewItem(obj.Key, 1);
                    item.Tag = obj;
                    item.Name = obj.Key;
                    ListViewItem.ListViewSubItem storageSubItem = new ListViewItem.ListViewSubItem(item, obj.StorageClass);
                    ListViewItem.ListViewSubItem lastModifiedSubItem = new ListViewItem.ListViewSubItem(item, obj.LastModified.ToShortDateString());
                    lastModifiedSubItem.Tag = obj.LastModified;
                    ListViewItem.ListViewSubItem sizeSubItem = new ListViewItem.ListViewSubItem(item, obj.Size);

                    subItems = new ListViewItem.ListViewSubItem[]
                                {
                                    storageSubItem, 
                                    lastModifiedSubItem,
                                    sizeSubItem
                                };

                    item.SubItems.AddRange(subItems);
                    FileList.Items.Add(item);
                }

                FileList.EndUpdate();
            }

            FileList.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
        }

        private void CancelButton_Click(object sender, EventArgs e)
        {
            base.DialogResult = DialogResult.Cancel;
        }

        private void FileList_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (FileList.SelectedIndices.Count > 0)
            {
                //TODO: Add support for multiple item selection
                CurrentObject = FileList.SelectedItems[0].Tag as S3Object;
            }
            else
            {
                CurrentObject = null;
            }
        }

        private void InsertButton_Click(object sender, EventArgs e)
        {
            string currentUrl = _s3Wrapper.GetUrl(CurrentBucket.Name, CurrentObject.Key);
            _htmlFragment = String.Format("<a href=\"{0}\">{1}</a>", currentUrl, CurrentObject.Key);
            base.DialogResult = DialogResult.OK;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string currentUrl = _s3Wrapper.GetUrl(CurrentBucket.Name, CurrentObject.Key);
            if (!string.IsNullOrEmpty(CloudFrontUrl.Text))
            {
                currentUrl = string.Format("{0}/{1}", CloudFrontUrl.Text, CurrentObject.Key);
            }
            _htmlFragment = String.Format("<img src=\"{0}\" />", currentUrl);
            base.DialogResult = DialogResult.OK;
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            S3Bucket bucket = e.Argument as S3Bucket;
            if (bucket != null)
            {
                IList<S3Object> objects = null;
                if (_s3Cache.ContainsKey(bucket.Name))
                {
                    objects = _s3Cache[bucket.Name];
                }
                else
                {
                    objects = _s3Wrapper.ListBucket(bucket.Name);
                    _s3Cache.Add(bucket.Name, objects);
                }

                e.Result = objects;
            }
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            BucketListBox.Enabled = true;

            // Populate the listview
            IList<S3Object> contents = e.Result as IList<S3Object>;
            if ((contents != null) && (contents.Count > 0))
            {
                PopulateObjectListView(contents);
            }

            this.Cursor = Cursors.Default;
        }

        private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
        }

        #region Sorting

        private void FileList_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            // Determine if clicked column is already the column that is being sorted.
            if (e.Column == _listViewSorter.SortColumn)
            {
                // Reverse the current sort direction for this column.
                if (_listViewSorter.Order == SortOrder.Ascending)
                {
                    _listViewSorter.Order = SortOrder.Descending;
                }
                else
                {
                    _listViewSorter.Order = SortOrder.Ascending;
                }
            }
            else
            {
                // Set the column number that is to be sorted; default to ascending.
                _listViewSorter.SortColumn = e.Column;
                _listViewSorter.Order = SortOrder.Ascending;
            }

            // Perform the sort with these new sort options.
            FileList.Sort();
        }

        #endregion Sorting

        #region Upload New Object

        private void AddButton_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                DoAdd(openFileDialog.FileName);
            }
        }

        private void DoAdd(string localFile)
        {
            string contentType = string.Empty;

            if (CurrentBucket == null)
            {
                return;
            }

            string key = String.Empty;
            if (!_context.AutoUploadWithFileName)
            {
                // Prompt for the key name
                using (KeyPrompt prompt = new KeyPrompt())
                {
                    prompt.FilePath = localFile;
                    if (prompt.ShowDialog() == DialogResult.OK)
                    {
                        key = prompt.Key;
                        if (prompt.OverrideContentType)
                        {
                            contentType = prompt.ContentType;
                        }
                        // Oh, and if they said "don't show me this again", set the value
                        _context.AutoUploadWithFileName = prompt.AutoUploadFileName;
                    }
                }
            }
            else
            {
                key = Utilities.GetKeyFromFilePath(localFile);
            }

            if (String.IsNullOrEmpty(key))
            {
                // No key was set, get out!
                return;
            }

            // Upload the file using the specified key
            using (FileUploadProgress uploadDialog = new FileUploadProgress())
            {
                uploadDialog.SetUploadInfo(_s3Wrapper, CurrentBucket, key, localFile, contentType);
                if (uploadDialog.ShowDialog() == DialogResult.OK)
                {
                    // Refresh the bucket list
                    LoadBucketContents(CurrentBucket, true);
                }
            }
        }

        #endregion Upload New Object

        #region Drag and Drop

        private void FileList_DragEnter(object sender, DragEventArgs e)
        {
            S3Bucket bucket = FileList.Tag as S3Bucket;
            if ((bucket != null) && e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void FileList_DragDrop(object sender, DragEventArgs e)
        {
            S3Bucket bucket = FileList.Tag as S3Bucket;
            if ((bucket != null) && e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] fileNames = e.Data.GetData(DataFormats.FileDrop) as string[];
                if ((fileNames != null) && (fileNames.Length > 0))
                {
                    this.Activate();
                    // For now, we only process the first file in the list.
                    DoAdd(fileNames[0]);
                }
            }
        }

        #endregion Drag and Drop

        private void backgroundWorkerBucketLoader_DoWork(object sender, DoWorkEventArgs e)
        {
            ThreeSharpWrapper wrapper = e.Argument as ThreeSharpWrapper;
            if (wrapper != null)
            {
                IList<S3Bucket> buckets = wrapper.ListAllBuckets();
                e.Result = buckets;
            }
        }

        private void backgroundWorkerBucketLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            IList<S3Bucket> buckets = e.Result as IList<S3Bucket>;
            LoadBuckets(buckets);
        }

        #region Delete Object

        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Delete the currently selected object
            DoDelete(CurrentObject);
        }

        private void DoDelete(S3Object s3Object)
        {
            if (s3Object != null)
            {
                try
                {
                    _s3Wrapper.DeleteObject(s3Object.Bucket.Name, s3Object.Key);
                    // Refresh the list
                    LoadBucketContents(CurrentBucket, true);
                }
                catch (WebException wex)
                {
                    if (MessageBox.Show("The selected item was not deleted due to an error.\nDo you want to see the error details?", "Delete Failed", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.Yes)
                    {
                        MessageBox.Show(wex.Message + "\n\n" + wex.StackTrace, "Delete Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        #endregion Delete Object

        private void RefreshButton_Click(object sender, EventArgs e)
        {
            // Force a reload of the currently displayed bucket
            S3Bucket bucket = FileList.Tag as S3Bucket;
            if (bucket != null)
            {
                LoadBucketContents(bucket, true);
            }
        }

        private void pictureBox3_Click(object sender, EventArgs e)
        {
            using (AboutBox abt = new AboutBox())
            {
                abt.ShowDialog();
            }
        }

        private void pictureBox2_DoubleClick(object sender, EventArgs e)
        {
            CloudFrontUrl.Visible = true;
            if (BucketListBox.SelectedItem != null)
            {
                CloudFrontUrl.Text = string.Concat("http://", BucketListBox.SelectedItem.ToString());
            }
        }
    }

    public class BucketListBoxItem
    {
        private string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        private object _tag;

        public object Tag
        {
            get { return _tag; }
            set { _tag = value; }
        }

        public BucketListBoxItem(string text)
        {
            _text = text;
        }

        public override string ToString()
        {
            return _text;
        }
    }
}